'''
Program Assignment:	Assignment 2
Name:			Alberto Massa                                                                      
Date:			11/08/2010                  
Description:            This file contains general procedures and classes for handle coding languages and syntax abstrac tree.
'''

'''
Listing Contents:
Compilation instructions
	This module does not contain directly runnable code
                                                                        
Class and procedures declarations:                                                                                     	
	Singleton: usefull metaclass
	LanguageFactory, Language: classes for handle generic coding languages informations
	Token: base node for abstract syntax tree
	Utils_Visitor: iterator for extrapolating usefull information on Token tree
'''

from random import random as rand

class Singleton(type):
	'''
	Metaclass for handle Singleton Pattern in python 2.6
	sounds usefull and completly reusable.
	Usage:
		class MySingleton(BaseClass):
			_metaclass_ = Singleton
			def __init__(self): pass
			def foo(self): pass

		s1 = MySingleton()
		s2 = MySingleton()
		assert(id(s1)==id(s2))
	Note:
		not thinked for been modified in Borg pattern.
		should be done with list of instance, mayebe
	'''
	def __init__(cls, name, bases, dict):
		super(Singleton, cls).__init__(name, bases, dict)
		cls.instance = None

	def __call__(cls, *a, **k):
		if cls.instance is None:
			cls.instance = super(Singleton, cls).__call__(*a, **k)
			return cls.instance
		else:
			return cls.instance


class LanguageFactory():
	'''
	Repository for handle multiples Language objects.
	'''
	__metaclass__ = Singleton
	def __init__(self):
		self.iid = rand() # singleton debug
		self.langs = dict()

	def addlang(self, language):
		'''
		Add Language object to handle.
		'''
		for ext in language.extentions:
			self.langs[ext]=language
	
	def getlang(self, ext):
		'''
		Given a string representing an extention (like 'py' for python file) get the Language object if it's already been added.
		'''
		return self.langs[ext]

	def clear(self):
		'''
		Clear all pre-added langs.
		'''
		self.langs = dict()

	def is_known_ext(self, ext):
		'''
		Return True if the ext corresponds to an added Language object, False otherwise.
		'''
		if ext in self.langs.keys():
			return True
		else:
			return False

class Language():
	'''
	Class for storing infos about a coding language.
	'''

	def __init__(self, name, exts, visitor_class):
		'''
		Take in input name string, list of strings for extentions of files, parser class (not instance) for contructing a simple abstract syntax tree.
		'''
		self.name = name
		self.extentions = exts
		self.visitor = visitor_class()

	def __repr__(self):
		s = "Language: {0}, exts:{1}, visitor object:{2}".format(
				self.name,
				" ".join(self.extentions),
				self.visitor)
		return s

class Token():
	'''
	Class that represent a node of syntax abstract tree.
	'''

	def __init__(self, name, parent, depth):
		'''
		Inputs: string with name of token, parent Token instance (could be None), depth of tree.
		'''
		self.name = name
		self.depth = depth
		self.parent = parent
		self.childs = []
		self.line = 0
		#self.dump() # for debug
	
	def __len__(self):
		'''
		Return as the length the number of childs (and childs of childs and os on).
		'''
		l = len(self.childs)
		for child in self.childs:
			# kind of recursion
			l+=len(child)
		return l

	def __repr__(self):
		string = "Token > \n\tname:{0} \n\tdepth:{1}, \n\tparent:{2}, \n\tchilds:{3}\n\tlines{4}\n".format(
			self.name,
			self.depth,
			self.parent and self.parent.name or '-',
			map(lambda x: x.name, self.childs),
			self.line)
		return string

	def addchild(self, n):
		'''
		Add a Token instance as child.
		'''
		self.childs.append(n)

	def dump(self):
		'''
		Print on std output the representations, and recursivly call dump() on all childs.
		'''
		print(self)
		for c in self.childs: c.dump()

	def accept(self, visitor):
		'''
		Call visit() method on visitor instance.
		'''
		visitor.visit(self)

class Utils_Visitor():
	'''
	Utility visitor for token objects.
	Define a callback properties that must contains None or a function.
	callbeck must be defined as:
		def call(token):
			do_something_with_token_instance()
	'''

	callback = None
		
	def visit(self, token):
		'''
		Start visit all the tree from given token.
		Is a top-down visit.
		'''
		self.callback(token)
		for child in token.childs:
			self.visit(child)

	def count_function(self, root):
		'''
		Utility for count all nodes in the tree.
		Take root node, return an integer.
		'''
		def increment(foo_token):
			increment.n+=1
		increment.n = 0 
		self.callback = increment
		root.accept(self)
		return increment.n

	def total_loc(self, root):
		'''
		Return total loc of a root Token object.
		'''
		loc = 0
		return root.line

